package com.itcast.beans.ioc;

import org.apache.commons.beanutils.BeanUtils;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * beanFactory的核心实现类
 * @作者 itcast
 * @创建日期 2020/6/18 14:29
 **/
public class DefaultListableBeanFactory implements BeanFactory{
    // 存放注册信息的集合
    private Map<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    // 存放单例对象的集合
    private Map<String,Object> singletonObjects = new ConcurrentHashMap<>();
    /**
     * 实现核心方法
     *  1. 从单例集合中获取是否存在对象 存在直接返回
     *  2. 查看注册列表中是否包含 该bean的定义 未包含报错
     *  3. 包含查看是否是单例的bean
     *      单例的bean
     *          创建对象并存入到单例集合
     *      多例的bean
     *          直接创建对象返回
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {
        Object singletonObject = singletonObjects.get(beanName);
        if(singletonObject!=null){
            return singletonObject;
        }

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if(beanDefinition==null){
            throw new RuntimeException("为找到 "+beanName +" 对应的bean定义");
        }

        String scope = beanDefinition.getScope();
        if("singleton".equals(scope)){
            // 如果是单例对象
            synchronized (this){
                Object obj = singletonObjects.get(beanName);
                if(obj==null){
                    obj=createBean(beanName);
                    singletonObjects.put(beanName,obj);
                }
                return obj;
            }
        } else {
            // 如果是多例对象
            return createBean(beanName);
        }
    }

    /**
     * 创建对象思路:
     *      1. 获取bean定义对象beanDefinition
     *      2. 获取bean的className信息
     *      3. 通过反射API获取对应class对象
     *      4. 通过反射API使用无参构造器创建对应对象
     *      5. 实现属性的依赖注入
     * @param beanName
     * @return
     */
    public Object createBean(String beanName){
        // 获取bean的定义信息
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        // 获取class信息
        String className = beanDefinition.getClassName();
        Class<?> aClass;
        try {
            aClass = Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException("未找到制定的class类==>"+e.getMessage());
        }
        // 使用无参构造器构建对象
        Object obj;
        try {
            obj = aClass.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new RuntimeException("实例化对象失败==>"+e.getMessage());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException("非法访问异常==>"+e.getMessage());
        }

        // 实现属性依赖注入
        List<Property> properties = beanDefinition.getProperties();
        for (Property property : properties) {
            String name = property.getName();
            String value = property.getValue();
            String ref = property.getRef();
            if (value!=null && !"".equals(value)){
                Map<String,Object> parmMap = new HashMap<>();
                parmMap.put(name,value);
                try {
                    BeanUtils.populate(obj,parmMap);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
            if(ref!=null && !"".equals(ref)){
                try {
                    BeanUtils.setProperty(obj,name,this.getBean(ref));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
        return obj;
    }

    /**
     * 将beanDefinition注册到容器中
     * @param beanName
     * @param beanDefinition
     */
    public void registerBeanDefinition(String beanName,BeanDefinition beanDefinition){
        this.beanDefinitionMap.put(beanName,beanDefinition);
    }

    /**
     * 初始化所有单例对象
     */
    public void preInstantiateSingletons(){
        beanDefinitionMap.forEach((beanName,beanDefiniton)->{
            String scope = beanDefiniton.getScope();
            if("singleton".equals(scope)){
                // 初始化单例对象
                this.getBean(beanName);
            }
        });
    }
}
